package main

import "fmt"

type Command interface {
	Execute()
}

type StartCommand struct {
	mb *MotherBoard
}

func NewStartCommand(mb *MotherBoard) *StartCommand {
	return &StartCommand{
		mb: mb,
	}
}

func (c *StartCommand) Execute() {
	c.mb.Start()
}

type RebootCommand struct {
	mb *MotherBoard
}

func NewRebootCommand(mb *MotherBoard) *RebootCommand {
	return &RebootCommand{
		mb: mb,
	}
}

func (c *RebootCommand) Execute() {
	c.mb.Reboot()
}

type MotherBoard struct{}

func (*MotherBoard) Start() {
	fmt.Print("system starting\n")
}

func (*MotherBoard) Reboot() {
	fmt.Print("system rebooting\n")
}

type Box struct {
	button1 Command
	button2 Command
}

func NewBox(button1, button2 Command) *Box {
	return &Box{
		button1: button1,
		button2: button2,
	}
}

func (b *Box) PressButton1() {
	b.button1.Execute()
}

func (b *Box) PressButton2() {
	b.button2.Execute()
}

func main() {
	/*
		创建一个有start和reboot指令的对象MotherBoard
		mb赋值给NewStartCommand的属性，NewStartCommand的Execute方法调用了MotherBoard的start方法，
		mb赋值给NewRebootCommand的属性，NewRebootCommand的Execute方法调用了MotherBoard的reboot方法，
		NewStartCommand，NewRebootCommand 赋值给Box，Box的PressButton1和PressButton2方法分别调用NewStartCommand和NewRebootCommand的Execute方法。
		总结：将多个命令装配成一个组合命令，即宏命令。
	*/
	mb := &MotherBoard{}
	startCommand := NewStartCommand(mb)
	rebootCommand := NewRebootCommand(mb)

	box1 := NewBox(startCommand, rebootCommand)
	box1.PressButton1()
	box1.PressButton2()

	box2 := NewBox(rebootCommand, startCommand)
	box2.PressButton1()
	box2.PressButton2()

	// Output:
	// system starting
	// system rebooting
	// system rebooting
	// system starting
}

/*
命令模式

命令（Command）模式的定义如下：
	将一个请求封装为一个对象，使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通，这样方便将命令对象进行储存、传递、调用、增加与管理。


命令模式的主要优点如下：
	降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。
	增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类，它满足“开闭原则”，对扩展比较灵活。
	可以实现宏命令。命令模式可以与组合模式结合，将多个命令装配成一个组合命令，即宏命令。
	方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合，实现命令的撤销与恢复。


其缺点是：
	可能产生大量具体命令类。因为计对每一个具体操作都需要设计一个具体命令类，这将增加系统的复杂性。


# 命令模式

命令模式本质是把某个对象的方法调用封装到对象中，方便传递、存储、调用。
示例中把主板单中的启动(start)方法和重启(reboot)方法封装为命令对象，再传递到主机(box)对象中。于两个按钮进行绑定：

* 第一个机箱(box1)设置按钮1(button1) 为开机按钮2(button2)为重启。
* 第二个机箱(box1)设置按钮2(button2) 为开机按钮1(button1)为重启。

从而得到配置灵活性。


除了配置灵活外，使用命令模式还可以用作：

* 批处理
* 任务队列
* undo, redo

等把具体命令封装到对象中使用的场合


*/
